package io.github.enbool.service;

import java.security.Principal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;

import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import io.github.enbool.bean.dto.UserRegisterDTO;
import io.github.enbool.bean.form.UserForm;
import io.github.enbool.bean.form.UserInforForm;
import io.github.enbool.bean.query.UserQuery;
import io.github.enbool.domain.AbstractUser;
import io.github.enbool.domain.Organization;
import io.github.enbool.domain.User;
import io.github.enbool.enums.ResultEnum;
import io.github.enbool.exception.RecordNotFoundException;
import io.github.enbool.exception.UserException;
import io.github.enbool.repository.UserRepository;
import io.github.enbool.util.CheckUtil;
import io.github.enbool.util.PermissionCheckUtil;
import io.github.enbool.util.PropertyUtil;
import io.github.enbool.util.UserUtil;


@Service
public class UserService {

	@Autowired
	private UserRepository userRepository;
	@Autowired
	private PasswordEncoder BCryptPasswordEncoder;
	@Autowired
	private OrganizationService organizationService;
	
	public User findOne(Long id) {
		User user = userRepository.findOne(id);
		if(user == null) {
			throw new RecordNotFoundException("User", id);
		}
		return user;
	}
	
	public User findByUsernameOrPhone(String name) {
		User user = userRepository.findByUsernameOrPhone(name, name);
		if(user == null) {
			throw new UserException(ResultEnum.USER_NOT_EXIST);
		}
		return user;
	}
	
	public Boolean existed(String name) {
		User user = userRepository.findByUsernameOrPhone(name, name);
		return user != null;
	}
	
	public User save(User user) {
		if(user.getId() == null) {
			user.setCreateTime(Instant.now().toEpochMilli());
		}
		user.setUpdateTime(Instant.now().toEpochMilli());
		
		return userRepository.save(user);
	}
	
	public Page<User> findByOrganizationId(Pageable pageable){
	
		Long organizationId = UserUtil.getUserOrganization().getId();
		return userRepository.findByOrganizationId(organizationId, pageable);
	}
	
	public Boolean enableUser(Long id, Boolean enabled) {
		userRepository.enableUser(id, enabled);
		return true;
	}
	
	/**
	 * 卖家启用或禁用用户
	 * @param id
	 * @param enabled
	 */
	public Boolean sellerEnableUser(Long id, Boolean enabled) {		
		User toEnableUser = findOne(id);
		PermissionCheckUtil.checkOrganization(toEnableUser.getOrganization().getId());
		
		userRepository.enableUser(id, enabled);
		return true;
	}
	
	/**
	 * 注册帐号，帐号为禁用状态，需管理员先启用
	 * @param userRegisterDTO
	 * @return
	 */
	public Boolean register(UserRegisterDTO userRegisterDTO) {
		CheckUtil.isPhone(userRegisterDTO.getPhone());
		CheckUtil.notNull(userRegisterDTO.getPassword(), "密码不能为空");
		CheckUtil.notNull(userRegisterDTO.getUsername(), "用户名不能为空");
		
		// boolean validated = smsValidateCodeProcessor.validate(userRegisterDTO.getPhone(), userRegisterDTO.getCode());
		// if(validated) {
			User user = new User();
			user.setUsername(userRegisterDTO.getUsername());
			user.setPassword(BCryptPasswordEncoder.encode(userRegisterDTO.getPassword()));
			user.setPhone(userRegisterDTO.getPhone());
			user.setDeleted(false);
			user.setEnabled(true);
			save(user);
		// }
		return true;
	}
	
	/**
	 * 重置密码
	 * @param userResetPasswordDTO
	 * @return
	 */
	/*public Boolean resetPassword(UserResetPasswordDTO userResetPasswordDTO) {
		CheckUtil.isPhone(userResetPasswordDTO.getPhone());
		CheckUtil.notNull(userResetPasswordDTO.getPassword(), "密码不能为空");
		
		boolean validated = smsValidateCodeProcessor.validate(userResetPasswordDTO.getPhone(), userResetPasswordDTO.getCode());
		if(validated) {
			User user = findByUsernameOrPhone(userResetPasswordDTO.getPhone());		
			user.setPassword(BCryptPasswordEncoder.encode(userResetPasswordDTO.getPassword()));			
			save(user);
		}
		return true;
	}*/
	
	/**
	 * 更新个人信息
	 * @param userInforForm
	 * @return
	 */
	public Boolean updateUserInfo(UserInforForm userInforForm) {
		
		User currentUser = (User) UserUtil.getUser();
		
		BeanUtils.copyProperties(userInforForm, currentUser, PropertyUtil.getNullPropertyNames(userInforForm));
		
		save(currentUser);
		
		return true;
	}
	
	/**
	 * 获取当前用户
	 * @param request
	 * @return
	 */
	public User getCurrentUser(HttpServletRequest request) {
		
		User user = findByPrincipal(request);
		return user;
	}
	
	/**
	 * 获取当前用户
	 * @param request
	 * @return
	 */
	public User me() {
		User user = (User) UserUtil.getUser();
		return user;
	}
	
	/**
	 * 修改密码
	 * @param newPassword
	 */
	public Boolean updatePassword(String newPassword) {
		User user = (User) UserUtil.getUser();
		user.setPassword(BCryptPasswordEncoder.encode(newPassword));
		save(user);
		return true;
	}
	
	
	/**
	 * 添加用户
	 * @param userForm
	 * @return
	 */
	public User createUser(UserForm userForm) {
		CheckUtil.isPhone(userForm.getPhone());
		
		User user = new User(userForm);
		user.setEnabled(true);
		user.setDeleted(false);
		user.setOrganization(UserUtil.getUserOrganization());
		user = save(user);
		return user;
	}
	
	public Boolean updateUserOrganization(Long id, Long organizationId){
		Organization organization = organizationService.findOne(organizationId);
		User user = findOne(id);
		user.setOrganization(organization);
		save(user);
		return true;
	}
	
	public Page<User> query(UserQuery query, Pageable pageable){
		
		Specification<User> specification = new Specification<User>() {
			
			@Override
			public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
				
				List<Predicate> predicates = new ArrayList<>();
					
				if(StringUtils.isNotBlank(query.getUsername())) {
					predicates.add(cb.equal(root.<String>get("username"), query.getUsername()));
				}				
				if(StringUtils.isNotBlank(query.getNickName())) {
					predicates.add(cb.like(root.<String>get("nickName"), "%" + query.getNickName() + "%"));
				}
				if(StringUtils.isNotBlank(query.getPhone())) {
					predicates.add(cb.equal(root.<String>get("phone"), query.getPhone()));
				}		
				
				if (query.getEnabled() != null) {
					predicates.add(cb.equal(root.<Boolean>get("enabled"), query.getEnabled()));
				}
				if (query.getDeleted() != null) {
					predicates.add(cb.equal(root.<Boolean>get("deleted"), query.getDeleted()));
				}
				
				if (query.getStart() != null) {
					predicates.add(cb.ge(root.<Long>get("createTime"), query.getStart()));
				}
				if (query.getEnd() != null) {
					predicates.add(cb.le(root.<Long>get("createTime"), query.getEnd()));
				}
				
				return cb.and(predicates.toArray(new Predicate[predicates.size()]));
			}
		};
		
		return userRepository.findAll(specification, pageable);	
	}
	
	private User findByPrincipal(HttpServletRequest request) {
		Principal principal = request.getUserPrincipal();
		
		if(principal == null) {
			return null;
		}
		
		String username = principal.getName();
		
		if(username == null) {
			return null;
		}
		
		User user = userRepository.findByUsername(username);
		
		return user;
	}

	public AbstractUser loadUser(String name) {
		return userRepository.findByUsernameOrPhone(name, name);
	}
}
